class AnimationController {
	constructor() {
		this.colors = ["green", "red", "yellow"];
		this.bgs = this.colors.map((cls) => document.querySelector(`.${cls}-bg`));
		this.cups = this.colors.map((cls) => document.querySelector(`.${cls}-cup`));
		this.contents = this.colors.map((cls) =>
			document.querySelector(`.${cls}-content`)
		);
		this.main = document.querySelector("main");
		this.cupCtn = document.querySelector(".cup");
	}

	init() {
		let start = false;
		let smoothMoving = false;

		const clickInit = () => {
			const clickHandler = (e) => {
				if (smoothMoving) return;
				const el = e.target;
				const isLeft = this.cups[0] === el;
				const isRight = this.cups[2] === el;
				if (isLeft) {
					this.right();
					requestAnimationFrame(() => {
						start = false;
					});
				} else if (isRight) {
					this.left();
					requestAnimationFrame(() => {
						start = false;
					});
				}
			};
			this.cups.forEach((cup) => {
				cup.addEventListener("mousedown", clickHandler.bind(this));
				cup.addEventListener("touchstart", clickHandler.bind(this));
			});
		};
		const touchInit = () => {
			let rafId,
				startX,
				totalW,
				isLeft,
				rate = null;
			const directionCls = ["left-right", "right-left"];
			const root = document.documentElement;
			const solveCubicBezier = (yTarget, x1, y1, x2, y2, epsilon = 1e-6) => {
				// 计算 y(t)
				function y(t) {
					return 3 * (1 - t) ** 2 * t * y1 + 3 * (1 - t) * t ** 2 * y2 + t ** 3;
				}

				// 计算 dy/dt
				function dyDt(t) {
					return (
						3 * (1 - t) ** 2 * y1 +
						6 * (1 - t) * t * (y2 - y1) +
						3 * t ** 2 * (1 - y2)
					);
				}

				// 牛顿迭代法
				let t = 0.5; // 初始猜测
				for (let i = 0; i < 100; i++) {
					const yt = y(t);
					const error = yt - yTarget;
					if (Math.abs(error) < epsilon) break;
					const dy = dyDt(t);
					if (Math.abs(dy) < 1e-6) break; // 避免除以零
					t -= error / dy;
					t = Math.max(0, Math.min(1, t)); // 限制 t 在 [0,1]
				}

				return {
					t,
					x: 3 * (1 - t) ** 2 * t * x1 + 3 * (1 - t) * t ** 2 * x2 + t ** 3,
				};
			};
			const helper = {
				getX: (e) => {
					if (e.touches) {
						return e.touches[0].clientX;
					}
					return e.clientX;
				},
				setColors: (start, end) => {
					root.style.setProperty("--start-color", `var(--${start})`);
					root.style.setProperty("--end-color", `var(--${end})`);
				},
				smoothMove: (rate, toMax = true, duration = 400) => {
					return new Promise((resolve) => {
						rate = solveCubicBezier(rate, 0.85, 0.88, 0.45, 1.5).x;
						const per =
							(toMax ? 0.9999 : 0 - rate) / (toMax ? duration : duration / 2);

						const start = performance.now();
						let last = start;
						const loop = () => {
							const now = performance.now();
							if (now - start >= duration) {
								smoothMoving = false;
								return resolve();
							}
							rate = Math.min(rate + per * (now - last), 0.9999);
							document.body.style.setProperty("--animation-delay", `-${rate}s`);
							requestAnimationFrame(loop.bind(this));
							last = now;
						};

						smoothMoving = true;
						loop();
					});
				},
			};

			const evtStart = (e) => {
				e.preventDefault();
				if (smoothMoving) return;
				start = true;
				startX = helper.getX(e);
			};

			this.cups.forEach((cup) => {
				cup.addEventListener("mousedown", evtStart);
				cup.addEventListener("touchstart", evtStart);
			});

			const evtMove = (e) => {
				if (!start) return;
				cancelAnimationFrame(rafId);
				rafId = requestAnimationFrame(() => {
					const x = helper.getX(e);
					const diffX = x - startX;
					// 计算方向和总宽度、设置颜色
					if (!totalW && Math.abs(diffX)) {
						let dirCls;
						const ctnRect = this.main.getBoundingClientRect();
						if (x > startX) {
							isLeft = false;
							dirCls = directionCls[0];
							totalW = ctnRect.right - x;
							helper.setColors(this.colors[1], this.colors[0]);
						} else {
							isLeft = true;
							dirCls = directionCls[1];
							totalW = x - ctnRect.left;
							helper.setColors(this.colors[1], this.colors[2]);
						}
						startX = x;
						this.main.classList.add(dirCls);
					}
					if (!totalW) return;
					// 计算移动比例
					let min, max;
					if (isLeft) {
						min = -0.9999;
						max = 0;
					} else {
						min = 0;
						max = 0.9999;
					}
					rate = Math.abs(Math.max(Math.min(diffX / totalW, max), min));
					document.body.style.setProperty("--animation-delay", `-${rate}s`);
				});
			};
			this.main.addEventListener("mousemove", evtMove);
			this.main.addEventListener("touchmove", evtMove);

			const resetMainAndRoot = () => {
				this.main.classList.remove(...directionCls);
				this.main.style.removeProperty("--animation-delay");
				this.main.classList.remove("ending");
				root.style.removeProperty("--start-color");
				root.style.removeProperty("--end-color");
			};
			const resetState = () => {
				start = false;
				totalW = null;
				isLeft = null;
				rate = null;
			};
			const evtEnd = () => {
				if (!start) return;
				cancelAnimationFrame(rafId);
				if (rate === null) {
					resetState();
					return;
				}
				// 将动画函数改为回弹
				this.main.classList.add("ending");
				// 移动比率不够 0.4
				if (rate < 0.4) {
					helper.smoothMove(rate, false).then(() => {
						resetMainAndRoot();
					});
				}
				// 左右滑动
				else {
					const func = (isLeft ? this.left : this.right).bind(this);
					helper.smoothMove(rate, true).then(() => {
						resetMainAndRoot();
						func(true);
					});
				}
				// 还原状态
				resetState();
			};
			window.addEventListener("mouseup", evtEnd.bind(this));
			window.addEventListener("touchend", evtEnd.bind(this));
		};

		clickInit();
		touchInit();
	}

	changePos(noEnter = true) {
		// 变换背景
		const bgCls = document.body.classList;
		bgCls.remove(...this.colors);
		bgCls.add(this.colors[1]);
		// 给背景水果单独添加入场动画
		if (!noEnter) {
			this.bgs[1].classList.add("enter");
			setTimeout(() => {
				this.bgs[1].classList.remove("enter");
			});
		}
		// 变化背景图片，移动杯子和内容
		const [imgClsLs, cupClsLs, cttClsLs] = [[], [], []];
		const clsLs = ["left", "active", "right"];
		this.cups.forEach((cup, i) => {
			imgClsLs[i] = this.bgs[i].classList;
			cupClsLs[i] = cup.classList;
			cttClsLs[i] = this.contents[i].classList;
			imgClsLs[i].remove(...clsLs);
			cupClsLs[i].remove(...clsLs);
			cttClsLs[i].remove(...clsLs);
		});
		clsLs.forEach((cls, i) => {
			imgClsLs[i].add(cls);
			cupClsLs[i].add(cls);
			cttClsLs[i].add(cls);
		});
	}

	left(noEnter = false) {
		// 移动颜色列表
		let [l, c, r] = this.colors;
		this.colors = [c, r, l];
		// 移动背景
		[l, c, r] = this.bgs;
		this.bgs = [c, r, l];
		// 移动杯子
		[l, c, r] = this.cups;
		this.cups = [c, r, l];
		// 移动内容
		[l, c, r] = this.contents;
		this.contents = [c, r, l];
		// 移除左侧内容动画
		l.classList.add("no-transition");
		setTimeout(() => {
			l.classList.remove("no-transition");
		});
		// 渲染
		this.changePos(noEnter);
	}

	right(noEnter = false) {
		// 移动颜色列表
		let [l, c, r] = this.colors;
		this.colors = [r, l, c];
		// 移动背景
		[l, c, r] = this.bgs;
		this.bgs = [r, l, c];
		// 移动杯子
		[l, c, r] = this.cups;
		this.cups = [r, l, c];
		// 移动内容
		[l, c, r] = this.contents;
		this.contents = [r, l, c];
		// 移除右侧内容动画
		r.classList.add("no-transition");
		setTimeout(() => {
			r.classList.remove("no-transition");
		});
		// 渲染
		this.changePos(noEnter);
	}
}

const controller = new AnimationController();
setTimeout(() => {
	controller.init();
});
